/*
 * TownMode.cpp
 *
 *  Created on: Sep 1, 2013
 *      Author: jason
 */

#include "../../../include/GameModes/TownMode/TownMode.hpp"
#include <utility>
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include "../../../include/GameModes/StateMachine.hpp"
#include "../../../include/Objects/Characters.hpp"
#include "../../../include/Objects/Treasure.hpp"
#include "../../../include/mapLoader.hpp"
#include "../../../include/Tileset/tileset.hpp"
#include "../../../include/Tileset/tilesetImage.hpp"
#include "../../../include/Tileset/tilesetLayer.hpp"
#include "../../../include/Tileset/tilesetObjectGroup.hpp"
#include "../../../include/Tileset/tilesetObject.hpp"
#include "../../../include/Tileset/tilesetPolygon.hpp"
#include "../../../include/Tileset/tilesetPolyline.hpp"
#include "../../../include/Tileset/tilesetProperties.hpp"
namespace OForce_Mode {

TownMode *TownMode::currentInstance = NULL;

TownMode::TownMode(std::string filename) : currentLayer(0),
		mFilename(filename),
		mMap(NULL),
		objectManager(NULL),
		dialogueManager(NULL),
		eventManager(NULL),
		coolDown(0),
		fadeTimer(0.0f),
		isFading(false),
		mFade(false)
		{
	currentState = CURRENT_STATE::EXPLORE;
	win = &Window;//_win;
	scriptFolder = "data/Scripts/";
	musicFolder = "data/Music/";
	mapFolder = "data/Maps/";
	source.x = 1;
	source.y = Down;
	if (!statImage.loadFromFile("./data/characterTextBox.png"))
		std::cout << "Could not load character box.\n";
	statImage.createMaskFromColor(sf::Color(19, 95, 181), 100);
	statTexture.loadFromImage(statImage);
	statSprite.setTexture(statTexture);
	statSprite.setScale(0.5, 0.7);
	view.setViewport(sf::FloatRect(0,0, 1.0f, 1.0f));
	// Set up town
	sf::Vector2i halfScreen = screenDimensions/2;
	objectManager = new ObjectManager();
	dialogueManager = new DialogueManager();
	eventManager = new EventManager();
	load();
	mInput = OForce_Input::InputEngine::Instance();
}

TownMode::~TownMode() {
	townScript.closeAllTables();
	townScript.closeFile();
	if (mMap)
		delete mMap;
	mMap = NULL;
	if (objectManager)
		delete objectManager;
	objectManager = NULL;
	if (dialogueManager)
		delete dialogueManager;
	dialogueManager = NULL;
	if (eventManager)
		delete eventManager;
	eventManager = NULL;
}

void TownMode::load() {
	// Set current Town Instance
	currentInstance = this;
	// Load Town's Lua Script
	std::string scriptFilename = scriptFolder;
	scriptFilename.append(mFilename);
	if (!townScript.openFile(scriptFilename.append(".lua"))) {
		return;
	}
	std::cout << "Loading town script\n";
//	// Reset table namespace in case an old version of the script is loaded.
//	std::string reset = mFilename;
//	reset.append(" = {}");
//	luaL_dostring(LuaManager::Instance()->getLuaState(), reset.c_str());
	townScript.openTable(mFilename);
	// Get audio files
	townScript.readDataVector<std::string> ("sound_filenames", sound_filenames);
	townScript.readDataVector("music_filenames", music_filenames);
	std::string musicFile = musicFolder + music_filenames[0];
	if (!mMusic.openFromFile(musicFile)) {
			std::cout << "Cannot find music file " << musicFile << "\n";
	}
	mMusic.play();
	mMusic.setLoop(true);
	try {
		luabind::object townTable(luabind::from_stack(townScript.getLuaState(),-1));
		luabind::object function = townTable["Init"];
		luabind::call_function<void>(function, this);
	} catch (luabind::error &e) {
		std::string error = lua_tostring(e.state(), -1);
		std::cout << error << std::endl;
	}
	scriptUpdateFunction = townScript.ReadFunctionPointer("update");
	// Load tilemap
	std::string mapFilename = mapFolder;
	// Script and TMX file should have same basename
	mapFilename.append(mFilename);
	mMap = new Tmx::Map(mapFilename.append(".tmx"));
	mMap->parse();
	setupObjects(mMap);
	worldSize = sf::Vector2i((mMap->getWidth()-1)*mMap->getTileWidth(),
			(mMap->getHeight()-1)*mMap->getTileHeight());
	townScript.closeAllTables();
}

void TownMode::addCharacter(OForce_Object::Character *character)
{
	objectManager->vCharacters.push_back(character);
	objectManager->vObject.push_back(character);
	objectManager->objectMap.insert(
			std::make_pair(character->getID(), character));
}

void TownMode::addTreasure(OForce_Object::Treasure *treasure)
{
	objectManager->vTreasure.push_back(treasure);
	objectManager->vObject.push_back(treasure);
	objectManager->objectMap.insert(
			std::make_pair(treasure->getID(), treasure));
}

void TownMode::addTriggerArea(TriggerArea *trig)
{
	objectManager->vTrigger.push_back(trig);
}

void TownMode::drawMap()
{
  bool horiz, vert;
  float width, height;
  int lid; // local id = GID - tileset.getFirstGID()
  sf::Vector2i imPos;    // position in the image to which the GID refers.
  sf::Vector2i numTiles; // number of tiles in current image
  // Draw the layers
  for (int i = 0; i < mMap->getNumLayers(); i++) {
    Tmx::Layer *layer = mMap->getLayer(i);
    for (int y = 0; y < layer->getHeight(); y++) {
	  for (int x = 0; x < layer->getWidth(); x++) {
		  int gid = layer->getTileId(x,y);
		  int tilesetIndex = mMap->FindTilesetIndex(gid);
		  Tmx::Tileset *tileset = 0;
		  if (tilesetIndex >= 0) {
			  tileset = mMap->getTileSet(tilesetIndex);
			  lid = gid - tileset->getFirstGid();
			  Tmx::Image *tilesetImage = tileset->getImage();
			  width = tileset->getTileWidth();
			  height = tileset->getTileHeight();
			  numTiles.x = tilesetImage->getWidth() / width;
			  numTiles.y = tilesetImage->getHeight() / height;
			  imPos.y = lid/numTiles.x;
			  imPos.x = lid - (numTiles.x * imPos.y);
			  if (layer->isTileFlippedHorizontally(x,y))
				  horiz = true;
			  else
				  horiz = false;
			  if (layer->isTileFlippedVertically(x,y))
				  vert = true;
			  else
				  vert = false;
			  if (layer->isTileFlippedDiagally(x,y)) {
				  horiz = true;
				  vert = true;
			  }
			  tilesetImage->tileSprite.setPosition(x*height, y*width);
			  tilesetImage->tileSprite.setTextureRect(
					  sf::IntRect(imPos.x*width, imPos.y*height, width, height));
			  //	    	 tilesetImage->tileSprite->FlipX(horiz);
			  //	    	 tilesetImage->tileSprite->FlipY(vert);
			  // TODO in navigationMode where the worlds are quite large,
			  //   put each world section in vectors and check to see if
			  //   the vector is within the view. If so, draw.
			  win->draw(tilesetImage->tileSprite);
		}
	  } // for (int x
	} // for (int y
    objectManager->drawLayer(i);
//    for (int curChar = 0; curChar < objectManager->vCharacters.size(); curChar++) {
//    	if (i == objectManager->vCharacters[curChar]->getCurrentLayer()) {
//    		// TODO check to see if we are within the viewport
//    		objectManager->vCharacters[curChar]->draw();
//    	}
//    }
  } // for (int i
} // void TownMode::drawMap()

void TownMode::characterAction()
{
	if (currentState == EXPLORE) {
		if (mInput->UpState() && mInput->RightState()) {
			if (playerCharacter->getDirection() != OForce_Object::UP) {
				source.y = Up;
				playerCharacter->updateAnim(OForce_Object::UP);
			}
			if (playerCharacter->sprite.getPosition().y > 0) {
				playerCharacter->sprite.move(0, -moveSpeed*dt*0.707);
				if (objectManager->collisionCheck(playerCharacter, 0)) {
					playerCharacter->sprite.move(0, moveSpeed*dt*0.707);
				}
			}
			if (playerCharacter->sprite.getPosition().x < worldSize.x) {
				playerCharacter->sprite.move(moveSpeed*dt*0.707, 0);
				if (objectManager->collisionCheck(playerCharacter, 0))
					playerCharacter->sprite.move(-moveSpeed*dt*0.707,0);
			}
		} else if (mInput->UpState() && mInput->LeftState()) {
			if (playerCharacter->getDirection() != OForce_Object::UP) {
				source.y = Up;
				playerCharacter->updateAnim(OForce_Object::UP);
			}
			if (playerCharacter->sprite.getPosition().y > 0) {
				playerCharacter->sprite.move(0, -moveSpeed*dt*0.707);
				if (objectManager->collisionCheck(playerCharacter, 0)) {
					playerCharacter->sprite.move(0, moveSpeed*dt*0.707);
				}
			}
			if (playerCharacter->sprite.getPosition().x > 0) {
				playerCharacter->sprite.move(-moveSpeed*dt*0.707, 0);
				if (objectManager->collisionCheck(playerCharacter, 0))
					playerCharacter->sprite.move(moveSpeed*dt*0.707,0);
			}
		} else if (mInput->DownState() && mInput->RightState()) {
			if (playerCharacter->getDirection() != OForce_Object::DOWN) {
				source.y = Down;
				playerCharacter->updateAnim(OForce_Object::DOWN);
			}
			if (playerCharacter->sprite.getPosition().y < worldSize.y) {
				playerCharacter->sprite.move(0, moveSpeed*dt*0.707);
				if (objectManager->collisionCheck(playerCharacter, 0)) {
					playerCharacter->sprite.move(0, -moveSpeed*dt*0.707);
				}
			}
			if (playerCharacter->sprite.getPosition().x < worldSize.x) {
				playerCharacter->sprite.move(moveSpeed*dt*0.707, 0);
				if (objectManager->collisionCheck(playerCharacter, 0))
					playerCharacter->sprite.move(-moveSpeed*dt*0.707,0);
			}
		} else if (mInput->DownState() && mInput->LeftState()) {
			if (playerCharacter->getDirection() != OForce_Object::DOWN) {
				source.y = Down;
				playerCharacter->updateAnim(OForce_Object::DOWN);
			}
			if (playerCharacter->sprite.getPosition().y < worldSize.y) {
				playerCharacter->sprite.move(0, moveSpeed*dt*0.707);
				if (objectManager->collisionCheck(playerCharacter, 0)) {
					playerCharacter->sprite.move(0, -moveSpeed*dt*0.707);
				}
			}
			if (playerCharacter->sprite.getPosition().x > 0) {
				playerCharacter->sprite.move(-moveSpeed*dt*0.707, 0);
				if (objectManager->collisionCheck(playerCharacter, 0))
					playerCharacter->sprite.move(moveSpeed*dt*0.707,0);
			}
		} else if (mInput->UpState()) {
			if (playerCharacter->getDirection() != OForce_Object::UP) {
				source.y = Up;
				playerCharacter->updateAnim(OForce_Object::UP);
			}
			if (playerCharacter->sprite.getPosition().y > 0) {
				playerCharacter->sprite.move(0, -moveSpeed*dt);
				if (objectManager->collisionCheck(playerCharacter, 0)) {
					playerCharacter->sprite.move(0,moveSpeed*dt);
				}
			}
		} else if (mInput->DownState()) {
			if (playerCharacter->getDirection() != OForce_Object::DOWN) {
				source.y = Down;
				playerCharacter->updateAnim(OForce_Object::DOWN);
			}
			if (playerCharacter->sprite.getPosition().y < worldSize.y) {
				playerCharacter->sprite.move(0, moveSpeed*dt);
				if (objectManager->collisionCheck(playerCharacter, 0))
					playerCharacter->sprite.move(0,-moveSpeed*dt);
			}
		} else if (mInput->LeftState()) {
			if (playerCharacter->getDirection() != OForce_Object::LEFT) {
				source.y = Left;
				playerCharacter->updateAnim(OForce_Object::LEFT);
			}
			if (playerCharacter->sprite.getPosition().x > 0) {
				playerCharacter->sprite.move(-moveSpeed*dt, 0);
				if (objectManager->collisionCheck(playerCharacter, 0))
					playerCharacter->sprite.move(moveSpeed*dt,0);
			}
		} else if (mInput->RightState()) {
			if (playerCharacter->getDirection() != OForce_Object::RIGHT) {
				source.y = Right;
				playerCharacter->updateAnim(OForce_Object::RIGHT);
			}
			if (playerCharacter->sprite.getPosition().x < worldSize.x) {
				playerCharacter->sprite.move(moveSpeed*dt, 0);
				if (objectManager->collisionCheck(playerCharacter, 0))
					playerCharacter->sprite.move(-moveSpeed*dt,0);
			}
		}
	}
	if (mInput->ActionState() && coolDown <= 0.0f) {
		if (currentState == EXPLORE) {
			// Action command
			OForce_Object::Object *obj = objectManager->findInteractable(playerCharacter, 25.0f);
			if (obj != NULL) {
				if (obj->getObjectType() == OForce_Object::CHARACTER_TYPE) {
					OForce_Object::Character *character = reinterpret_cast<OForce_Object::Character *> (obj);
					character->initiateDialogue();
				}
				else if (obj->getObjectType() == OForce_Object::TREASURE_TYPE) {
					OForce_Object::Treasure *treasure = reinterpret_cast<OForce_Object::Treasure *> (obj);
					treasure->open(playerCharacter);
					currentState = TREASURE;
					currentTreasure = treasure;
				}
			} // else { // open menu
		} else if (currentState == DIALOGUE) {
//			dialogueManager->beginLine();
			if (dialogueManager->finishedTyping()) {
				coolDown = 0.2f;
			} else {
				dialogueManager->hurry();
			}
		} else if (currentState == TREASURE) {
			if (currentTreasure->finishedTyping()) {
				currentState = EXPLORE;
				coolDown = 0.2f;
			} else {
				currentTreasure->hurry();
			}
		}
	}
//	// TODO get rid of this
//	switch (Event.key.code) {
//		case sf::Keyboard::Add:
//			view.zoom(1.1f);
//			break;
//		case sf::Keyboard::Subtract:
//			view.zoom(0.9f);
//			break;
//	}
}

// Tmx::ObjectGroup organizes static collision objects
// non-static collision objects will be controlled via Lua
void TownMode::setupObjects(Tmx::Map *map)
{
  sf::Vector2f pos;
  sf::Vector2f size;
  for (int i = 0; i < map->getNumObjectGroups(); i++) {
    Tmx::ObjectGroup *objectGroup = map->getObjectGroup(i);
	for (int j = 0; j < objectGroup->getNumObjects(); j++) {
	  Tmx::Object *object = objectGroup->getObject(j);
	  pos.x = object->getX();
	  pos.y = object->getY();
	  size.x = object->getWidth();
	  size.y = object->getHeight();
	  Tmx::Polygon *polygon = object->getPolygon();
	  sf::ConvexShape sfPolygon;
	  if (polygon != 0) {
		sfPolygon.setPointCount(polygon->getNumPoints());
	    for (int k = 0; k < polygon->getNumPoints(); k++) {
		  Tmx::Point point = polygon->getPoint(k);
		  sfPolygon.setPoint(k, sf::Vector2f((float)point.x, (float)point.y));
		}
      }
	  Tmx::Polyline *polyline = object->getPolyline();
	  if (polyline != 0) {
		sfPolygon.setPointCount(polyline->getNumPoints());
	    for (int k = 0; k < polyline->getNumPoints(); k++) {
		  Tmx::Point point = polyline->getPoint(k);
		  sfPolygon.setPoint(k, sf::Vector2f((float)point.x, (float)point.y));
		}
		objectManager->collisionPolys.push_back(sfPolygon);
      }
	  if (sfPolygon.getPointCount() == 0) {
	    sf::FloatRect rectObj(pos, size);
	    objectManager->collisionRects.push_back(rectObj);
	  }
	} // for (int j
  } // for (int i
}

// This should be implemented in each game mode
void TownMode::drawBasicUI()
{
	//TODO this is just a basic UI text.
	// should be moved into a class that specializes for basic UI
	sf::Vector2f viewCenter = view.getCenter();
	sf::Vector2f viewSize = view.getSize();
	statSprite.setPosition(viewCenter.x+20,viewCenter.y-viewSize.y/2+10);
	win->draw(statSprite);
	sf::Text charStats;
	charStats.setFont(font);
	charStats.setCharacterSize(18);
	std::string charStatsString = playerCharacter->getName();
	charStatsString.append("\t\tLevel 1\n\nHP: 15/15\n\nMP:8/8");
	charStats.setString(charStatsString);
	charStats.setPosition(viewCenter.x+50,viewCenter.y-viewSize.y/2+30);
	win->draw(charStats);
	charStatsString = playerCharacter->getClass();
	charStats.setString(charStatsString);
	charStats.setPosition(viewCenter.x+viewSize.x/2-100,viewCenter.y-viewSize.y/2+30);
	win->draw(charStats);
}

void TownMode::update()
{
	dt = gameClock.restart().asSeconds();
	frameCounter += frameSpeed * dt;
	float fps = 1.0f / (dt);
	std::cout << "FPS: " << fps << "\r";
	if (coolDown > 0.0) {
		coolDown -= dt;
	}
	characterAction();
	if (scriptUpdateFunction.is_valid()) {
		luabind::call_function<void>(scriptUpdateFunction);
	}
//	for (int curChar = 0; curChar < objectManager->vCharacters.size(); curChar++) {
////		if (frameCounter >= switchFrame) {
//			objectManager->vCharacters[curChar]->update();
////		}
//	}
	objectManager->update();
	eventManager->update();
	if (frameCounter >= switchFrame) {
		frameCounter = 0;
	}
	if (currentState == DIALOGUE) {
		dialogueManager->update();
	} else if (currentState == TREASURE) {
		currentTreasure->update();
	}
//	if (mFade) {
//		fadeTimer -= dt;
//		if (fadeTimer <= 0.0) {
//			isFading = false;
//			fadeTimer = 0.0f;
//		}
//		fadeRect.setFillColor(sf::Color(0,0,0,255*fadeTimer/0.75f));
//	}
}

void TownMode::draw() {
// TODO this alpha blinking will be moved to the movement area in battle mode
// once that is implemented
//  // alpha for blinking
//	phi += dt*k;
//	if (phi > two_pi)
//		phi = 0.0f;
//	curAlpha = (int)(30.0+28.0*cos(phi));
	sf::Vector2f cameraCenter = sf::Vector2f(camera->getFrameSizeX()/2,
			camera->getFrameSizeY()/2);
	view.setCenter(camera->sprite.getPosition()+cameraCenter);
	win->setView(view);
	drawMap();
	drawBasicUI();
//	objectManager->draw();
	if (currentState == DIALOGUE) {
		dialogueManager->draw();
	} else if (currentState == TREASURE) {
		currentTreasure->drawDialogue();
	}
//	if (mFade) {
//		win->draw(fadeRect);
//	}
}

void TownMode::playTrack(int n)
{
	std::string musicFile = musicFolder + music_filenames[n];
	if (!mMusic.openFromFile(musicFile)) {
		std::cout << "Cannot find music file " << musicFile << "\n";
	}
	mMusic.play();
	mMusic.setLoop(true);
}

void TownMode::startFading()
{
	mMusic.stop();
//	sf::Vector2f viewCenter = view.getCenter();
//	sf::Vector2f viewSize = view.getSize();
//	fadeRect.setPosition(viewCenter.x-viewSize.x, viewCenter.y-viewSize.y);
//	viewSize.x *= 2;
//	viewSize.y *= 2;
//	fadeRect.setSize(viewSize);
//	fadeRect.setFillColor(sf::Color(0,0,0,255));
//	fadeTimer = 0.75f;
//	isFading = true;
//	mFade = true;
	StateMachine::Instance()->startFading();
}

} // namespace OForce_Mode
